﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks.Dataflow;
using System.Collections.Concurrent;


namespace ChuMeng
{
    public class DelayCall
    {
        public List<Coroutine> queue = new List<Coroutine>();

        public void Run()
        {
            var p = queue [0];
            queue.RemoveAt(0);
            if (p != null)
            {
                p.Run();
            } else
            {
                Console.WriteLine("WaitForSecond");
            }
        }

        public void AddCall(Coroutine c, int t)
        {
            for (int i = 0; i < t; i++)
            {
                queue.Add(null);
            }
            Console.WriteLine("AddCall ");
            queue.Add(c);
        }

        public int Count()
        {
            Console.WriteLine("CountIs " + queue.Count);
            return queue.Count;
        }
    }

    public class Coroutine
    {
        Coroutine waitFor;
//当前等待执行的协程
        Coroutine continueWhenFinished;
        //执行完成之后需要执行协程
        IEnumerator runObj;
        //上下文

        public void Run()
        {
            var ret = runObj.MoveNext();
            if (!ret)
            {
                if (continueWhenFinished != null)
                {
                    continueWhenFinished.waitFor = null;
                    var c = continueWhenFinished;
                    continueWhenFinished = null;
                    c.Run();
                }
                return;
            }

            ProcessCoroutineCurrent();
        }

        public class WaitForSecond
        {
            public int time;

            public WaitForSecond(int t)
            {
                time = t;
            }
        }

        void ProcessCoroutineCurrent()
        {
            var cur = runObj.Current;
            Console.WriteLine("CurrentValue " + cur + " currentActor " + Actor.actor.Value.Id + " threadId " + Thread.CurrentThread.ManagedThreadId);
            if (cur == null)
            {
                Actor.actor.Value.delayCall.AddCall(this, 0);
                return;
            }
            HandleIEnumerableCurrentReturnValue(cur);
        }

        void HandleIEnumerableCurrentReturnValue(object cur)
        {
            Console.WriteLine("CurActor " + Actor.actor.Value.Id);
            if (cur.GetType() == typeof(WaitForSecond))
            {
                Actor.actor.Value.delayCall.AddCall(this, (cur as WaitForSecond).time);
                return;
            }
            Console.WriteLine("after waitForsec");
            if (cur.GetType() == typeof(Coroutine))
            {
                var child = cur as Coroutine;
                waitFor = child;
                child.continueWhenFinished = this;
                return;
            }
            Console.WriteLine("AddSelf To Delay");
            Actor.actor.Value.delayCall.AddCall(this, 0);
        }

        public static Coroutine StartCoroutine(IEnumerator ie)
        {
            var c = new Coroutine();
            c.runObj = ie;
            c.Run();
            return c;
        }
    }

    public static class ActorUtil
    {
        public static void SendMsg(this Actor target, string msg)
        {
            var m = new ActorMsg();
            m.msg = msg;
            target.mailbox.SendAsync(m);
        }

        public static void SendMsg(this Actor target, KBEngine.Packet packet)
        {
            var m = new ActorMsg(){ packet = packet };
            target.mailbox.SendAsync(m);
        }
    }

    public class ActorMsg
    {
        public string msg;
        public KBEngine.Packet packet;
    }

    public class Actor
    {
        public int Id;
        public DelayCall delayCall = new DelayCall();
        public static ThreadLocal<Actor> actor;

        public BufferBlock<ActorMsg> mailbox = new BufferBlock<ActorMsg>();
        protected bool isStop = false;

        public Actor()
        {
            //Init ();
        }

        private async Task Dispatch()
        {
            while (!isStop)
            {
                var msg = await mailbox.ReceiveAsync();
                Console.WriteLine("threadId receive " + this.GetType() + " id " + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("receive msg " + msg);
                await ReceiveMsg(msg);
            }

        }

        protected virtual async Task ReceiveMsg(ActorMsg msg){
            await Task.FromResult(default(object));
        }

        public void Init()
        {
            Task.Run(Dispatch);
        }
        public void Stop() {
            isStop = true;
        }
    }

	
	
	

    public class ActorManager
    {
        public static ActorManager Instance;
        Dictionary<int, Actor> actorDict;
        private int actId = 0;
        //public BlockingCollection <Type> createMsg;
        bool isStop = false;

        public ActorManager()
        {
            //createMsg = new BlockingCollection<Type>();
            actorDict = new Dictionary<int, Actor>();
            Instance = this;

            //var thread = new Thread(new ThreadStart(WaitCreateMsg));
            //thread.Start();
        }

        /*
        void WaitCreateMsg()
        {
            while (!createMsg.IsCompleted && !isStop)
            {
                var data = createMsg.Take();
                Console.WriteLine("receive create Msg " + data);
                var a = Activator.CreateInstance(data) as Actor;
                var id = AddActor(a);
                a.Init();
            }
        }
        */

        public int AddActor(Actor act)
        {
            if(isStop){
                return -1;
            }
            var id = Interlocked.Increment(ref actId);
            lock (actorDict)
            {
                actorDict.Add(id, act);
            }
            act.Id = id;
            act.Init();
            return id;
        }
        public void RemoveActor(int id){
            lock(actorDict){
                actorDict.Remove(id);
            }
        }

        public Actor GetActor(int key)
        {
            Actor ret = null;
            lock (actorDict)
            {
                var ok = actorDict.TryGetValue(key, out ret);
            }
            return ret;
        }

        public T GetActor<T>() where T : Actor {
            T ret = null;
            lock(actorDict){
                foreach(var a in actorDict){
                    if(a.GetType() == typeof(T)) {
                        ret = (T)a.Value;
                        break;
                    }
                }
            }
            return ret;
        }


        public void Stop()
        {
            isStop = true;
            lock(actorDict){
                foreach(var act in actorDict){
                    act.Value.Stop();
                }
            }
        }
    }
}
